Introduction

# load in the libraries
library(stringi)
library(plotly)
Loading required package: ggplot2
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout
library(plyr)

Attaching package: ‘plyr’

The following objects are masked from ‘package:plotly’:

    arrange, mutate, rename, summarise
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ──────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.0 ──
✓ tibble  2.1.3     ✓ dplyr   0.8.3
✓ tidyr   1.0.0     ✓ stringr 1.4.0
✓ readr   1.3.1     ✓ forcats 0.4.0
✓ purrr   0.3.3     
── Conflicts ─────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::arrange()   masks plyr::arrange(), plotly::arrange()
x purrr::compact()   masks plyr::compact()
x dplyr::count()     masks plyr::count()
x dplyr::failwith()  masks plyr::failwith()
x dplyr::filter()    masks plotly::filter(), stats::filter()
x dplyr::id()        masks plyr::id()
x dplyr::lag()       masks stats::lag()
x dplyr::mutate()    masks plyr::mutate(), plotly::mutate()
x dplyr::rename()    masks plyr::rename(), plotly::rename()
x dplyr::summarise() masks plyr::summarise(), plotly::summarise()
x dplyr::summarize() masks plyr::summarize()

Analysis

Read in the data

Read in data from files

beer = read.csv("./data/Beers.csv",header = TRUE)
breweries = read.csv("./data/Breweries.csv",header = TRUE, strip.white = TRUE)

#display the dataframes
beer
breweries
NA

How many breweries are there in each state?

Heat Map of Breweies per State

## add lowercase state name for heat map
lstates = tolower(state.name)
state_count = state_breweies %>% 
    add_rownames("region") %>% 
    mutate( region=lstates[match(State, state.abb)] )
Deprecated, use tibble::rownames_to_column() instead.
## find center of each state for text position 
snames <- data.frame(region=lstates, long=state.center$x, lat=state.center$y)
snames <- merge(snames, state_count, by="region")

## merge map data with counts data
choro <- left_join(
  map_data("state"), 
  state_count
)
Joining, by = "region"
ggplot(choro, aes(long, lat)) +
  geom_polygon(aes(group = group, fill = n)) + 
  geom_text(data=snames, aes(long, lat, label=n)) +
  coord_quickmap()

NA
NA

The range of breweries per state ranges from 1 to 47, with Colorado holding the highest number of breweries per state. The heat map above shows North and South Dakotas along with West Virginia as having the lowest number of breweries (1 per state). Following Colorado is California with 39 and Oregon with 29 breweries. There appears to be higher number of breweries in the north east and west coast, as opposed to the central states, with the exception of Colorado and Texas.

Cleaning the data

Prepering/transforming the data into a usable form for analysis, visualization, etc… #### Merging Dataframes Merge beer data with the breweries data. Print the first 6 observations and the last six observations to check the merged file.


attach(beer)
beer[order(Brewery_id),] # sort the data to determine column for merge

# merge on Brewery ID
breweries_named <- plyr::rename(breweries, c("Brew_ID"="Brewery_id"))

brewing_beer <- merge(breweries_named,beer,by="Brewery_id", all=TRUE) # outter join

brewed_beer <- plyr::rename(brewing_beer, c("Name.x"="Brewery", "Name.y"="Beer")) # rename breweries and beer

head(brewed_beer,6) # show the first 6 rows of data
NA

Missing Data

Missing data are in columns ABV (62) and IBU (1005) only. Cleaning data in multiple options: 1. complete records only 2. replacing NA with the averages of the remainder of the column


colSums(is.na(averaged_beer))
Brewery_id    Brewery       City      State       Beer    Beer_ID        ABV        IBU      Style     Ounces ABVpercent 
         0          0          0          0          0          0          0          0          0          0          0 

Median Alcohol Content

Median of Alcohol by Volume and Bitterness by State

# group by state, get median of ABV, IBU
medians <- as.data.frame(aggregate(completed_beer[,c(7,8)], by=list(completed_beer$State), FUN=median)) 
median_df <- plyr::rename(medians, c("Group.1"="State")) # rename the column to State

median_graph <- median_df %>% ggplot(aes(x = ABV, y = IBU, color=State)) + geom_point() + ggtitle("Median Alcohol Content and Bitterness by State") # plot scatter plot

ABV_bar <-ggplot(data=median_df, aes(x = State, y = ABV, fill = State)) +
  geom_bar(stat="identity", width = 0.75) + theme(axis.text.x = element_text(angle = 90, hjust = 1)) + ggtitle("Median ABV by State")
ggplotly(ABV_bar)


IBU_bar <-ggplot(data=median_df, aes(x = State, y = IBU, fill = State)) +
  geom_bar(stat="identity", width = 0.75) + theme(axis.text.x = element_text(angle = 90, hjust = 1)) + ggtitle("Median IBU by State")
ggplotly(IBU_bar)

NA

Max ABV and IBU

Which state has the maximum alcoholic (ABV) beer? Which state has the most bitter (IBU) beer?

# find the max ABV and IBU from each state
maximums <- as.data.frame(aggregate(completed_beer[,c(7,8)], by=list(completed_beer$State), FUN=max))
max_df <- plyr::rename(maximums, c("Group.1"="State")) # rename the column to State

#find the max ABV and IBU states
max_ABV <- max_df %>% filter(ABV == max(ABV))
max_IBU <- max_df %>% filter(IBU == max(IBU))

max_state <- rbind(max_ABV, max_IBU)
max_state
NA

Summary Statistics

The summary statistics and distribution of the ABV variable.

#Creates column representing ABV in percentage which is more user-readable and in-line with how
#ABV is represented by the industry
print("Values below are in percentage (%)")
[1] "Values below are in percentage (%)"
#Using method 1 for handling missing data
completed_beer$ABVpercent <- completed_beer$ABV*100 

summary(completed_beer$ABVpercent) #Range, quartiles, and mean of ABV percentage value
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.700   5.000   5.700   5.991   6.800  12.500 
cat("Standard deviation: ", sd(completed_beer$ABVpercent)) #Standard deviation of ABV percentage value
Standard deviation:  1.357633
#Using method 2 for handling missing data
averaged_beer$ABVpercent <- averaged_beer$ABV*100

summary(averaged_beer$ABVpercent)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  0.100   5.000   5.700   5.977   6.700  12.800 
cat("Standard deviation: ", sd(averaged_beer$ABVpercent))
Standard deviation:  1.336634

Relationship between Bitterness and Alcohol Content

Is there an apparent relationship between the bitterness of the beer and its alcoholic content? Draw a scatter plot. Make your best judgment of a relationship and EXPLAIN your answer.

#Uses method 1 for handling missing data (remove incomplete lines)

#Correlation between ABV percentage and IBU; function uses Pearson method as default
cor(x=completed_beer$ABVpercent, y=completed_beer$IBU)
[1] 0.6706215
#Visualizations between ABV percentage and IBU
ggplot(data=completed_beer, mapping=aes(x= IBU, y= ABVpercent, position_jitter())) + geom_point()


ggplot(data=completed_beer, mapping=aes(x= ABVpercent, y= IBU, position_jitter())) + geom_point()

#Uses method 2 for handling missing data (replace NAs with average of present data)

#Correlation between ABV percentage and IBU; function uses Pearson method as default
cor(x=averaged_beer$ABVpercent, y=averaged_beer$IBU)
[1] 0.520011
#Visualizations between ABV percentage and IBU
ggplot(data=averaged_beer, mapping=aes(x= IBU, y= ABVpercent, position_jitter())) + geom_point()


ggplot(data=averaged_beer, mapping=aes(x= ABVpercent, y= IBU, position_jitter())) + geom_point()

IPAs vs. Ales

Budweiser would also like to investigate the difference with respect to IBU and ABV between IPAs (India Pale Ales) and other types of Ale (any beer with “Ale” in its name other than IPA). You decide to use KNN classification to investigate this relationship. Provide statistical evidence one way or the other. You can of course assume your audience is comfortable with percentages … KNN is very easy to understand conceptually.

In addition, while you have decided to use KNN to investigate this relationship (KNN is required) you may also feel free to supplement your response to this question with any other methods or techniques you have learned. Creativity and alternative solutions are always encouraged.

ipa = brewed_beer[grep("IPA", brewed_beer$Style), ]
not_ipa = brewed_beer[-grep("IPA", brewed_beer$Style), ]
ales = not_ipa[grep("Ale", not_ipa$Style), ]

Additional inferences

Knock their socks off! Find one other useful inference from the data that you feel Budweiser may be able to find value in. You must convince them why it is important and back up your conviction with appropriate statistical evidence.

LS0tCnRpdGxlOiAiQnJld2luZyBVcCBhIFN0b3JtIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlCi0tLQojIyMgSW50cm9kdWN0aW9uCgpgYGB7cn0KIyBsb2FkIGluIHRoZSBsaWJyYXJpZXMKbGlicmFyeShzdHJpbmdpKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShwbHlyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgojIyBBbmFseXNpcwoKIyMjIFJlYWQgaW4gdGhlIGRhdGEKUmVhZCBpbiBkYXRhIGZyb20gZmlsZXMKYGBge3J9CmJlZXIgPSByZWFkLmNzdigiLi9kYXRhL0JlZXJzLmNzdiIsaGVhZGVyID0gVFJVRSkKYnJld2VyaWVzID0gcmVhZC5jc3YoIi4vZGF0YS9CcmV3ZXJpZXMuY3N2IixoZWFkZXIgPSBUUlVFLCBzdHJpcC53aGl0ZSA9IFRSVUUpCgojZGlzcGxheSB0aGUgZGF0YWZyYW1lcwpiZWVyCmJyZXdlcmllcwoKYGBgCgoKIyMjIEhvdyBtYW55IGJyZXdlcmllcyBhcmUgdGhlcmUgaW4gZWFjaCBzdGF0ZT8KCmBgYHtyfQoKc3RyKGJyZXdlcmllcykgIyBjaGVjayB0aGF0IFN0YXRlIGlzIGEgRmFjdG9yCgpzdGF0ZV9icmV3ZWllcyA8LSBicmV3ZXJpZXMgJT4lIGdyb3VwX2J5KFN0YXRlKSAlPiUgdGFsbHkoKSAjIGNvdW50IHRoZSBudW1iZXIgb2YgYnJld2VyaWVzIHdpdGhpbiBhIHN0YXRlCnVuaXF1ZV9zdGF0ZV9icmV3ZWllcyA8LSBicmV3ZXJpZXMgJT4lIGdyb3VwX2J5KFN0YXRlKSAlPiUgdGFsbHkobl9kaXN0aW5jdChOYW1lKSkgIyBjaGVjayBmb3IgYW55IGR1cGxpY2F0ZXMKCmBgYAoKIyMjIyBIZWF0IE1hcCBvZiBCcmV3ZWllcyBwZXIgU3RhdGUKCmBgYHtyfQojIyBhZGQgbG93ZXJjYXNlIHN0YXRlIG5hbWUgZm9yIGhlYXQgbWFwCmxzdGF0ZXMgPSB0b2xvd2VyKHN0YXRlLm5hbWUpCnN0YXRlX2NvdW50ID0gc3RhdGVfYnJld2VpZXMgJT4lIAogICAgYWRkX3Jvd25hbWVzKCJyZWdpb24iKSAlPiUgCiAgICBtdXRhdGUoIHJlZ2lvbj1sc3RhdGVzW21hdGNoKFN0YXRlLCBzdGF0ZS5hYmIpXSApCgojIyBmaW5kIGNlbnRlciBvZiBlYWNoIHN0YXRlIGZvciB0ZXh0IHBvc2l0aW9uIApzbmFtZXMgPC0gZGF0YS5mcmFtZShyZWdpb249bHN0YXRlcywgbG9uZz1zdGF0ZS5jZW50ZXIkeCwgbGF0PXN0YXRlLmNlbnRlciR5KQpzbmFtZXMgPC0gbWVyZ2Uoc25hbWVzLCBzdGF0ZV9jb3VudCwgYnk9InJlZ2lvbiIpCgojIyBtZXJnZSBtYXAgZGF0YSB3aXRoIGNvdW50cyBkYXRhCmNob3JvIDwtIGxlZnRfam9pbigKICBtYXBfZGF0YSgic3RhdGUiKSwgCiAgc3RhdGVfY291bnQKKQpnZ3Bsb3QoY2hvcm8sIGFlcyhsb25nLCBsYXQpKSArCiAgZ2VvbV9wb2x5Z29uKGFlcyhncm91cCA9IGdyb3VwLCBmaWxsID0gbikpICsgCiAgZ2VvbV90ZXh0KGRhdGE9c25hbWVzLCBhZXMobG9uZywgbGF0LCBsYWJlbD1uKSkgKwogIGNvb3JkX3F1aWNrbWFwKCkKCgpgYGAKVGhlIHJhbmdlIG9mIGJyZXdlcmllcyBwZXIgc3RhdGUgcmFuZ2VzIGZyb20gMSB0byA0Nywgd2l0aCBDb2xvcmFkbyBob2xkaW5nIHRoZSBoaWdoZXN0IG51bWJlciBvZiBicmV3ZXJpZXMgcGVyIHN0YXRlLiBUaGUgaGVhdCBtYXAgYWJvdmUgc2hvd3MgTm9ydGggYW5kIFNvdXRoIERha290YXMgYWxvbmcgd2l0aCBXZXN0IFZpcmdpbmlhIGFzIGhhdmluZyB0aGUgbG93ZXN0IG51bWJlciBvZiBicmV3ZXJpZXMgKDEgcGVyIHN0YXRlKS4gRm9sbG93aW5nIENvbG9yYWRvIGlzIENhbGlmb3JuaWEgd2l0aCAzOSBhbmQgT3JlZ29uIHdpdGggMjkgYnJld2VyaWVzLiBUaGVyZSBhcHBlYXJzIHRvIGJlIGhpZ2hlciBudW1iZXIgb2YgYnJld2VyaWVzIGluIHRoZSBub3J0aCBlYXN0IGFuZCB3ZXN0IGNvYXN0LCBhcyBvcHBvc2VkIHRvIHRoZSBjZW50cmFsIHN0YXRlcywgd2l0aCB0aGUgZXhjZXB0aW9uIG9mIENvbG9yYWRvIGFuZCBUZXhhcy4gCgojIyMgQ2xlYW5pbmcgdGhlIGRhdGEKUHJlcGVyaW5nL3RyYW5zZm9ybWluZyB0aGUgZGF0YSBpbnRvIGEgdXNhYmxlIGZvcm0gZm9yIGFuYWx5c2lzLCB2aXN1YWxpemF0aW9uLCBldGMuLi4gCiMjIyMgTWVyZ2luZyBEYXRhZnJhbWVzCk1lcmdlIGJlZXIgZGF0YSB3aXRoIHRoZSBicmV3ZXJpZXMgZGF0YS4gUHJpbnQgdGhlIGZpcnN0IDYgb2JzZXJ2YXRpb25zIGFuZCB0aGUgbGFzdCBzaXggb2JzZXJ2YXRpb25zIHRvIGNoZWNrIHRoZSBtZXJnZWQgZmlsZS4gIApgYGB7cn0KCmF0dGFjaChiZWVyKQpiZWVyW29yZGVyKEJyZXdlcnlfaWQpLF0gIyBzb3J0IHRoZSBkYXRhIHRvIGRldGVybWluZSBjb2x1bW4gZm9yIG1lcmdlCgojIG1lcmdlIG9uIEJyZXdlcnkgSUQKYnJld2VyaWVzX25hbWVkIDwtIHBseXI6OnJlbmFtZShicmV3ZXJpZXMsIGMoIkJyZXdfSUQiPSJCcmV3ZXJ5X2lkIikpCgpicmV3aW5nX2JlZXIgPC0gbWVyZ2UoYnJld2VyaWVzX25hbWVkLGJlZXIsYnk9IkJyZXdlcnlfaWQiLCBhbGw9VFJVRSkgIyBvdXR0ZXIgam9pbgoKYnJld2VkX2JlZXIgPC0gcGx5cjo6cmVuYW1lKGJyZXdpbmdfYmVlciwgYygiTmFtZS54Ij0iQnJld2VyeSIsICJOYW1lLnkiPSJCZWVyIikpICMgcmVuYW1lIGJyZXdlcmllcyBhbmQgYmVlcgoKaGVhZChicmV3ZWRfYmVlciw2KSAjIHNob3cgdGhlIGZpcnN0IDYgcm93cyBvZiBkYXRhCgpgYGAKCiMjIyMgTWlzc2luZyBEYXRhCk1pc3NpbmcgZGF0YSBhcmUgaW4gY29sdW1ucyBBQlYgKDYyKSBhbmQgSUJVICgxMDA1KSBvbmx5LiAKQ2xlYW5pbmcgZGF0YSBpbiBtdWx0aXBsZSBvcHRpb25zOgoxLiBjb21wbGV0ZSByZWNvcmRzIG9ubHkKMi4gcmVwbGFjaW5nIE5BIHdpdGggdGhlIGF2ZXJhZ2VzIG9mIHRoZSByZW1haW5kZXIgb2YgdGhlIGNvbHVtbgoKYGBge3J9CiMgc2VsZWN0aW5nIG9ubHkgY29tcGxldGUgY2FzZXMKd2hpY2goaXMubmEoYnJld2VkX2JlZXIpKSAjIGRldGVybWluZSB3aGljaCByb3dzIGNvbnRhaW4gTkEKY29sU3Vtcyhpcy5uYShicmV3ZWRfYmVlcikpICMgc3VtbWFyeSBvZiB0aGUgbnVtYmVyIG9mIE5BIGluIGVhY2ggY29sdW1uCmNvbXBsZXRlZF9iZWVyIDwtIGJyZXdlZF9iZWVyW2NvbXBsZXRlLmNhc2VzKGJyZXdlZF9iZWVyKSwgXSAjIGRmIHdpdGggb25seSBjb21wbGV0ZSByZWNvcmRzCgojIHJlcGxhY2luZyBOQSB3aXRoIGF2ZXJhZ2VzCmF2ZXJhZ2VkX2JlZXIgPC0gYnJld2VkX2JlZXIgIyBtYWtlIGEgZHVwbGljYXRlIG9mIHRoZSBvcmlnaW5hbCBkZiB0byBtYW5pcHVsYXRlCmF2ZXJhZ2VBQlYgPC0gbWVhbihhdmVyYWdlZF9iZWVyJEFCViwgbmEucm0gPSBUUlVFKSAjIHNldCBhdmVyYWdlIHZhcmlhYmxlIGZvciBBQlYKYXZlcmFnZUlCVSA8LSBtZWFuKGF2ZXJhZ2VkX2JlZXIkSUJVLCBuYS5ybSA9IFRSVUUpICMgc2V0IGF2ZXJhZ2UgdmFyaWFibGUgZm9yIElCVQoKI3JlcGxhY2UgTkEgdmFsdWVzIHdpdGggdGhlIGF2ZXJhZ2Ugb2YgdGhlIHByZXNlbnQgdmFsdWVzCmF2ZXJhZ2VkX2JlZXIkSUJVW2lzLm5hKGF2ZXJhZ2VkX2JlZXIkSUJVKV08LWF2ZXJhZ2VJQlUKYXZlcmFnZWRfYmVlciRBQlZbaXMubmEoYXZlcmFnZWRfYmVlciRBQlYpXTwtYXZlcmFnZUFCVgoKCmNvbFN1bXMoaXMubmEoYXZlcmFnZWRfYmVlcikpCgpgYGAKCiMjIyBNZWRpYW4gQWxjb2hvbCBDb250ZW50Ck1lZGlhbiBvZiBBbGNvaG9sIGJ5IFZvbHVtZSBhbmQgQml0dGVybmVzcyBieSBTdGF0ZQpgYGB7cn0KIyBncm91cCBieSBzdGF0ZSwgZ2V0IG1lZGlhbiBvZiBBQlYsIElCVQptZWRpYW5zIDwtIGFzLmRhdGEuZnJhbWUoYWdncmVnYXRlKGNvbXBsZXRlZF9iZWVyWyxjKDcsOCldLCBieT1saXN0KGNvbXBsZXRlZF9iZWVyJFN0YXRlKSwgRlVOPW1lZGlhbikpIAptZWRpYW5fZGYgPC0gcGx5cjo6cmVuYW1lKG1lZGlhbnMsIGMoIkdyb3VwLjEiPSJTdGF0ZSIpKSAjIHJlbmFtZSB0aGUgY29sdW1uIHRvIFN0YXRlCgptZWRpYW5fZ3JhcGggPC0gbWVkaWFuX2RmICU+JSBnZ3Bsb3QoYWVzKHggPSBBQlYsIHkgPSBJQlUsIGNvbG9yPVN0YXRlKSkgKyBnZW9tX3BvaW50KCkgKyBnZ3RpdGxlKCJNZWRpYW4gQWxjb2hvbCBDb250ZW50IGFuZCBCaXR0ZXJuZXNzIGJ5IFN0YXRlIikgIyBwbG90IHNjYXR0ZXIgcGxvdAoKQUJWX2JhciA8LWdncGxvdChkYXRhPW1lZGlhbl9kZiwgYWVzKHggPSBTdGF0ZSwgeSA9IEFCViwgZmlsbCA9IFN0YXRlKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5Iiwgd2lkdGggPSAwLjc1KSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsgZ2d0aXRsZSgiTWVkaWFuIEFCViBieSBTdGF0ZSIpCmdncGxvdGx5KEFCVl9iYXIpCgpJQlVfYmFyIDwtZ2dwbG90KGRhdGE9bWVkaWFuX2RmLCBhZXMoeCA9IFN0YXRlLCB5ID0gSUJVLCBmaWxsID0gU3RhdGUpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCB3aWR0aCA9IDAuNzUpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkgKyBnZ3RpdGxlKCJNZWRpYW4gSUJVIGJ5IFN0YXRlIikKZ2dwbG90bHkoSUJVX2JhcikKCmBgYAojIyMgTWF4IEFCViBhbmQgSUJVCldoaWNoIHN0YXRlIGhhcyB0aGUgbWF4aW11bSBhbGNvaG9saWMgKEFCVikgYmVlcj8gV2hpY2ggc3RhdGUgaGFzIHRoZSBtb3N0IGJpdHRlciAoSUJVKSBiZWVyPwpgYGB7cn0KIyBmaW5kIHRoZSBtYXggQUJWIGFuZCBJQlUgZnJvbSBlYWNoIHN0YXRlCm1heGltdW1zIDwtIGFzLmRhdGEuZnJhbWUoYWdncmVnYXRlKGNvbXBsZXRlZF9iZWVyWyxjKDcsOCldLCBieT1saXN0KGNvbXBsZXRlZF9iZWVyJFN0YXRlKSwgRlVOPW1heCkpCm1heF9kZiA8LSBwbHlyOjpyZW5hbWUobWF4aW11bXMsIGMoIkdyb3VwLjEiPSJTdGF0ZSIpKSAjIHJlbmFtZSB0aGUgY29sdW1uIHRvIFN0YXRlCgojZmluZCB0aGUgbWF4IEFCViBhbmQgSUJVIHN0YXRlcwptYXhfQUJWIDwtIG1heF9kZiAlPiUgZmlsdGVyKEFCViA9PSBtYXgoQUJWKSkKbWF4X0lCVSA8LSBtYXhfZGYgJT4lIGZpbHRlcihJQlUgPT0gbWF4KElCVSkpCgptYXhfc3RhdGUgPC0gcmJpbmQobWF4X0FCViwgbWF4X0lCVSkKbWF4X3N0YXRlCgpgYGAKCiMjIyBTdW1tYXJ5IFN0YXRpc3RpY3MKVGhlIHN1bW1hcnkgc3RhdGlzdGljcyBhbmQgZGlzdHJpYnV0aW9uIG9mIHRoZSBBQlYgdmFyaWFibGUuCgpgYGB7cn0KI0NyZWF0ZXMgY29sdW1uIHJlcHJlc2VudGluZyBBQlYgaW4gcGVyY2VudGFnZSB3aGljaCBpcyBtb3JlIHVzZXItcmVhZGFibGUgYW5kIGluLWxpbmUgd2l0aCBob3cKI0FCViBpcyByZXByZXNlbnRlZCBieSB0aGUgaW5kdXN0cnkKcHJpbnQoIlZhbHVlcyBiZWxvdyBhcmUgaW4gcGVyY2VudGFnZSAoJSkiKQoKI1VzaW5nIG1ldGhvZCAxIGZvciBoYW5kbGluZyBtaXNzaW5nIGRhdGEKY29tcGxldGVkX2JlZXIkQUJWcGVyY2VudCA8LSBjb21wbGV0ZWRfYmVlciRBQlYqMTAwIAoKc3VtbWFyeShjb21wbGV0ZWRfYmVlciRBQlZwZXJjZW50KSAjUmFuZ2UsIHF1YXJ0aWxlcywgYW5kIG1lYW4gb2YgQUJWIHBlcmNlbnRhZ2UgdmFsdWUKCmNhdCgiU3RhbmRhcmQgZGV2aWF0aW9uOiAiLCBzZChjb21wbGV0ZWRfYmVlciRBQlZwZXJjZW50KSkgI1N0YW5kYXJkIGRldmlhdGlvbiBvZiBBQlYgcGVyY2VudGFnZSB2YWx1ZQoKI1VzaW5nIG1ldGhvZCAyIGZvciBoYW5kbGluZyBtaXNzaW5nIGRhdGEKYXZlcmFnZWRfYmVlciRBQlZwZXJjZW50IDwtIGF2ZXJhZ2VkX2JlZXIkQUJWKjEwMAoKc3VtbWFyeShhdmVyYWdlZF9iZWVyJEFCVnBlcmNlbnQpCgpjYXQoIlN0YW5kYXJkIGRldmlhdGlvbjogIiwgc2QoYXZlcmFnZWRfYmVlciRBQlZwZXJjZW50KSkKCgpgYGAKCiMjIyBSZWxhdGlvbnNoaXAgYmV0d2VlbiBCaXR0ZXJuZXNzIGFuZCBBbGNvaG9sIENvbnRlbnQKSXMgdGhlcmUgYW4gYXBwYXJlbnQgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIGJpdHRlcm5lc3Mgb2YgdGhlIGJlZXIgYW5kIGl0cyBhbGNvaG9saWMgY29udGVudD8gRHJhdyBhIHNjYXR0ZXIgcGxvdC4gIE1ha2UgeW91ciBiZXN0IGp1ZGdtZW50IG9mIGEgcmVsYXRpb25zaGlwIGFuZCBFWFBMQUlOIHlvdXIgYW5zd2VyLgpgYGB7cn0KI1VzZXMgbWV0aG9kIDEgZm9yIGhhbmRsaW5nIG1pc3NpbmcgZGF0YSAocmVtb3ZlIGluY29tcGxldGUgbGluZXMpCgojQ29ycmVsYXRpb24gYmV0d2VlbiBBQlYgcGVyY2VudGFnZSBhbmQgSUJVOyBmdW5jdGlvbiB1c2VzIFBlYXJzb24gbWV0aG9kIGFzIGRlZmF1bHQKY29yKHg9Y29tcGxldGVkX2JlZXIkQUJWcGVyY2VudCwgeT1jb21wbGV0ZWRfYmVlciRJQlUpCgojVmlzdWFsaXphdGlvbnMgYmV0d2VlbiBBQlYgcGVyY2VudGFnZSBhbmQgSUJVCmdncGxvdChkYXRhPWNvbXBsZXRlZF9iZWVyLCBtYXBwaW5nPWFlcyh4PSBJQlUsIHk9IEFCVnBlcmNlbnQsIHBvc2l0aW9uX2ppdHRlcigpKSkgKyBnZW9tX3BvaW50KCkKCmdncGxvdChkYXRhPWNvbXBsZXRlZF9iZWVyLCBtYXBwaW5nPWFlcyh4PSBBQlZwZXJjZW50LCB5PSBJQlUsIHBvc2l0aW9uX2ppdHRlcigpKSkgKyBnZW9tX3BvaW50KCkKYGBgCgpgYGB7cn0KI1VzZXMgbWV0aG9kIDIgZm9yIGhhbmRsaW5nIG1pc3NpbmcgZGF0YSAocmVwbGFjZSBOQXMgd2l0aCBhdmVyYWdlIG9mIHByZXNlbnQgZGF0YSkKCiNDb3JyZWxhdGlvbiBiZXR3ZWVuIEFCViBwZXJjZW50YWdlIGFuZCBJQlU7IGZ1bmN0aW9uIHVzZXMgUGVhcnNvbiBtZXRob2QgYXMgZGVmYXVsdApjb3IoeD1hdmVyYWdlZF9iZWVyJEFCVnBlcmNlbnQsIHk9YXZlcmFnZWRfYmVlciRJQlUpCgojVmlzdWFsaXphdGlvbnMgYmV0d2VlbiBBQlYgcGVyY2VudGFnZSBhbmQgSUJVCmdncGxvdChkYXRhPWF2ZXJhZ2VkX2JlZXIsIG1hcHBpbmc9YWVzKHg9IElCVSwgeT0gQUJWcGVyY2VudCwgcG9zaXRpb25faml0dGVyKCkpKSArIGdlb21fcG9pbnQoKQoKZ2dwbG90KGRhdGE9YXZlcmFnZWRfYmVlciwgbWFwcGluZz1hZXMoeD0gQUJWcGVyY2VudCwgeT0gSUJVLCBwb3NpdGlvbl9qaXR0ZXIoKSkpICsgZ2VvbV9wb2ludCgpCmBgYAoKIyMjIElQQXMgdnMuIEFsZXMKQnVkd2Vpc2VyIHdvdWxkIGFsc28gbGlrZSB0byBpbnZlc3RpZ2F0ZSB0aGUgZGlmZmVyZW5jZSB3aXRoIHJlc3BlY3QgdG8gSUJVIGFuZCBBQlYgYmV0d2VlbiBJUEFzIChJbmRpYSBQYWxlIEFsZXMpIGFuZCBvdGhlciB0eXBlcyBvZiBBbGUgKGFueSBiZWVyIHdpdGgg4oCcQWxl4oCdIGluIGl0cyBuYW1lIG90aGVyIHRoYW4gSVBBKS4gIFlvdSBkZWNpZGUgdG8gdXNlIEtOTiBjbGFzc2lmaWNhdGlvbiB0byBpbnZlc3RpZ2F0ZSB0aGlzIHJlbGF0aW9uc2hpcC4gIFByb3ZpZGUgc3RhdGlzdGljYWwgZXZpZGVuY2Ugb25lIHdheSBvciB0aGUgb3RoZXIuIFlvdSBjYW4gb2YgY291cnNlIGFzc3VtZSB5b3VyIGF1ZGllbmNlIGlzIGNvbWZvcnRhYmxlIHdpdGggcGVyY2VudGFnZXMg4oCmIEtOTiBpcyB2ZXJ5IGVhc3kgdG8gdW5kZXJzdGFuZCBjb25jZXB0dWFsbHkuCgpJbiBhZGRpdGlvbiwgd2hpbGUgeW91IGhhdmUgZGVjaWRlZCB0byB1c2UgS05OIHRvIGludmVzdGlnYXRlIHRoaXMgcmVsYXRpb25zaGlwIChLTk4gaXMgcmVxdWlyZWQpIHlvdSBtYXkgYWxzbyBmZWVsIGZyZWUgdG8gc3VwcGxlbWVudCB5b3VyIHJlc3BvbnNlIHRvIHRoaXMgcXVlc3Rpb24gd2l0aCBhbnkgb3RoZXIgbWV0aG9kcyBvciB0ZWNobmlxdWVzIHlvdSBoYXZlIGxlYXJuZWQuICBDcmVhdGl2aXR5IGFuZCBhbHRlcm5hdGl2ZSBzb2x1dGlvbnMgYXJlIGFsd2F5cyBlbmNvdXJhZ2VkLiAgCmBgYHtyfQppcGEgPSBicmV3ZWRfYmVlcltncmVwKCJJUEEiLCBicmV3ZWRfYmVlciRTdHlsZSksIF0Kbm90X2lwYSA9IGJyZXdlZF9iZWVyWy1ncmVwKCJJUEEiLCBicmV3ZWRfYmVlciRTdHlsZSksIF0KYWxlcyA9IG5vdF9pcGFbZ3JlcCgiQWxlIiwgbm90X2lwYSRTdHlsZSksIF0KCmBgYAoKIyMjIEFkZGl0aW9uYWwgaW5mZXJlbmNlcwpLbm9jayB0aGVpciBzb2NrcyBvZmYhICBGaW5kIG9uZSBvdGhlciB1c2VmdWwgaW5mZXJlbmNlIGZyb20gdGhlIGRhdGEgdGhhdCB5b3UgZmVlbCBCdWR3ZWlzZXIgbWF5IGJlIGFibGUgdG8gZmluZCB2YWx1ZSBpbi4gIFlvdSBtdXN0IGNvbnZpbmNlIHRoZW0gd2h5IGl0IGlzIGltcG9ydGFudCBhbmQgYmFjayB1cCB5b3VyIGNvbnZpY3Rpb24gd2l0aCBhcHByb3ByaWF0ZSBzdGF0aXN0aWNhbCBldmlkZW5jZS4gCmBgYHtyfQoKYGBgCgoKCgoKCgoKCgo=